﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

using SharedGenomics.Core;
using SharedGenomics.Domain;
using SharedGenomics.Annotations;
using SharedGenomics.Workbench.PresentationModel;
using SharedGenomics.Workbench.Annotations;

namespace SharedGenomics.Workbench.Models
{
    public class AnalysisResultsModel : IAnalysisResultsModel
    {
        private readonly DataGridSource<AnalysisResult> _results;
        private readonly IAnalysisResultSetAnnotator _annotator;
        private AnalysisResultDataColumnSchema _currentColumnSchema;
        private SnpAnnotationRepository _annotationSets;

        public AnalysisResultsModel(IAnalysisResultSetAnnotator annotator)
        {
            this._results = new DataGridSource<AnalysisResult>();
            this._results.ListChanged += (o, e) => { this.ResultsChanged.Raise(this); };
            this._annotator = annotator;
        }

        public IList<WiggleElement> GetSortedWiggleElementListForResults(int[] resultIndices)
        {
            return this._results
                .TakeAtIndices(resultIndices)                                                                                   //take results at given indices
                .SelectMany(result => result.GetSnpPropertyValues().Select(snp => new { Snp = snp, P = result.P }))             //get all snp names from results and associated P values
                .Select(snpP => new { AnnotationSet = this._annotationSets[snpP.Snp], P = snpP.P })                             //get annotation set for snp name and keep P
                .Where(set => set != null)                                                                                      //remove any snps with no annotation set
                .Select(set => new { NcbiAnnotationInfo = set.AnnotationSet[AnnotationKey.NcbiSummary], P = set.P })            //get Ncbi annotation info and keep P
                .Where(summary =>                                                                                               //remove any annotations with no ncbi annotation
                    summary.NcbiAnnotationInfo != null
                    && summary.NcbiAnnotationInfo.Status == AnnotationRequestStatus.Finished)
                .Select(summary => new { Annotation = (NcbiAnnotation)summary.NcbiAnnotationInfo.Value, P = summary.P })        //Extract Ncbi annotation value and P
                .Where(summary =>                                                                                               //remove any annotations with missing or invalid chromosome, position or P values
                    summary.Annotation.ChromosomalPosition.HasValue
                    && summary.Annotation.Chromosome.HasValue
                    && !double.IsNaN(summary.P))
                .Select(summary => new
                {                                                                                        //create temporary class with required wiggle element properties
                    Chromosome = summary.Annotation.Chromosome.Value,
                    Position = summary.Annotation.ChromosomalPosition.Value,
                    NegativeLogP = -Math.Log10(summary.P)
                })
                .Select(vals => new WiggleElement(vals.Chromosome, vals.Position, vals.Position + 1, vals.NegativeLogP))        //create wiggle elements
                .OrderBy(wiggle => wiggle)                                                                                      //sort
                .ToList();
        }

        public IEnumerable<KeggPathwayFrequency> GetPathwayFrequenciesForResults(int[] resultIndices)
        {
            return this._results
                .TakeAtIndices(resultIndices)                                                                                   //take results at given indices                        
                .SelectMany(result => result.GetSnpPropertyValues())                                                            //get all snp names from results
                .Select(snp => this._annotationSets[snp])                                                                       //get annotation set for snp
                .Where(set => set != null)                                                                                      //remove null annotation sets (missing for snp)
                .Select(set => set[AnnotationKey.KeggPathway])                                                                  //get request for kegg pathway
                .Where(info => info != null && info.Status == AnnotationRequestStatus.Finished)                                 //keep only annotation requests which have completed
                .SelectMany(info => ((KeggPathwayAnnotation)info.Value).Pathways)                                               //select all pathways from annotations
                .Where(pathway => pathway.Name != null)                                                                         //remove pathways with null name
                .GroupBy(pathway => pathway.Name, StringComparer.InvariantCultureIgnoreCase)                                    //group pathways by name
                .Select(pathwayGroup => new KeggPathwayFrequency(pathwayGroup.Key, pathwayGroup.Count()))                       //create frequency for each group (i.e. pathway name)
                .ToList();
        }

        public bool TryGetAnnotationSetForValue(object value, out AnnotationInfoSet annotationSet)
        {
            annotationSet = null;
            string snpName = value as string;

            if (this._annotationSets == null) return false;

            annotationSet = this._annotationSets[snpName];
            return annotationSet != null;
        }

        public void LoadResultSet(IAnalysisResultSet results, AnalysisResultDataColumnSchema columnSchema)
        {
            results.ThrowIfNull("results");
            columnSchema.ThrowIfNull("columnSchema");

            this._currentColumnSchema = columnSchema;
            this._results.LoadItems(results.Cast<AnalysisResult>());

            this._annotationSets = this._annotator.Annotate(results);
        }

        public bool[] GetResultAnnotationSetCompletedStatuses()
        {
            if (this._annotationSets == null)
                return this._results.Select(ar => false).ToArray();

            return this._results.Select(result =>
            {
                var set = this._annotationSets[result.SnpName];
                return set != null && (set.Count - (set.FinishedCount + set.FailedCount)) == 0;
            }).ToArray();
        }

        public System.Collections.IEnumerable Results
        {
            get
            {
                return this._results.Cast<object>().ToList();
            }
        }

        public void SortResultsByColumn(string propertyName)
        {
            var properties = TypeDescriptor.GetProperties(this._currentColumnSchema.ResultType);
            var prop = properties.Find(propertyName, true);

            ((IBindingList)this._results).ApplySort(prop, ListSortDirection.Ascending);
        }

        public AnalysisResult this[int index]
        {
            get { return this._results[index]; }
        }

        public event EventHandler ResultsChanged;
    }
}
